/*
 * @Author: qinuoyun
 * @Date:   2021-09-23 22:50:03
 * @Last Modified by:   wiki
 * @Last Modified time: 2021-11-16 00:36:41
 */
const Schema = require('async-validator').default;
const {cloneDeep, get, isPlainObject} = require('lodash');

class RuleResult {
    constructor(valid, validator, message, invalidFields) {
        this.valid = valid
        this.message = message
        this.invalidFields = invalidFields
        this.validator = validator
    }
}

class Validator {
    /**
     * 自定义注册方法
     * @param type
     * @param validator
     */
    static register(type, validator) {
        Schema.register(type, validator)
    }

    /**
     * 验证规则
     * @param {object} data  context上下文
     * @param {boolean} thowError  是否抛出异常错误，默认false
     */
    validate(data, thowError) {
        const descriptor = this.descriptor
        const parameters = {body: data}
        this.parameters = cloneDeep(parameters)
        this.pasrsedParameters = cloneDeep(parameters)
        return new Promise((resolve, reject) => {
            if (descriptor && isPlainObject(descriptor) && Object.keys(descriptor).length > 0) {
                let validator = new Schema(descriptor)
                const model = this._getModel(descriptor)
                //验证
                validator.validate(model, {first: true, firstFields: true}, (errors, invalidFields) => {
                    let valid = !errors
                    let message = errors ? errors[0].message : '';
                    let result = new RuleResult(valid, this, message, invalidFields)
                    if (thowError && !valid) {
                        reject(result)
                    } else {
                        resolve(result)
                    }
                })
            } else {
                resolve(new RuleResult(true, this))
            }
        })
    }

    /**
     * 构建request model
     * @param {object} rules 验证规则
     */
    _getModel(descriptor) {
        let model = {}
        Object.keys(descriptor).forEach((key) => {
            const rules = descriptor[key]
            if (!Array.isArray(rules)) {
                throw new Error('验证规则必须为数组')
            }
            let value = this.findParam(key).value
            for (const rule of rules) {
                if (Object.prototype.hasOwnProperty.call(rule, 'type')) {
                    value = this._convert(rule.type, value)
                    break;
                }
            }
            model[key] = value
        })
        this.model = model
        return model
    }

    /**
     * 类型转换
     * @param value
     */
    _convert(type, value) {
        if (!value) {
            return value
        }

        let result = "";
        switch (type) {
            case 'number':
                result = parseInt(value)
                break;
            case 'string':
                result = String(value)
                break;
            case 'float':
                result = parseFloat(value)
                break;
            case 'boolean':
                result = !!value
                break;
            default:
                result = value
                break;
        }
        return result
    }

    /**
     * 寻找model
     * lodash get方法
     * @param {string} key
     */
    findParam(key) {
        let value
        value = get(this.parameters, ['query', key])
        if (value !== undefined) {
            return {
                value,
                path: ['query', key]
            }
        }
        value = get(this.parameters, ['body', key])
        if (value !== undefined) {
            return {
                value,
                path: ['body', key]
            }
        }
        value = get(this.parameters, ['params', key])
        if (value !== undefined) {
            return {
                value,
                path: ['params', key]
            }
        }
        value = get(this.parameters, ['header', key])
        if (value !== undefined) {
            return {
                value,
                path: ['header', key]
            }
        }
        return {
            value: null,
            path: []
        }
    }

    /**
     * 根据key获取参数值
     * @param {string} key
     */
    getValue(path) {
        if (!path) {
            return ''
        }
        //根据路径找，如果根据路径找不到，在根据key找
        const value = get(this.parameters, path, null)
        if (value == null && !path.includes('.')) {
            return this.model[path] || this.findParam(path).value
        }
        return value
    }


}

module.exports = {
    Validator,
    RuleResult
}